/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.hiruss.imageprocessor.util;

import java.awt.Color;
import java.awt.image.BufferedImage;

/**
 *
 * @author Hiru
 */
public class PointOperations {

    public static BufferedImage rotateLeft(BufferedImage source) {
        int w = source.getWidth();
        int h = source.getHeight();
        BufferedImage dest = new BufferedImage(h, w, source.getType());
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                dest.setRGB(i, w - 1 - j, source.getRGB(j, i));
            }
        }
        return dest;
    }

    public static BufferedImage rotateRight(BufferedImage source) {
        int w = source.getWidth();
        int h = source.getHeight();
        BufferedImage dest = new BufferedImage(h, w, source.getType());
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                dest.setRGB(h - 1 - i, j, source.getRGB(j, i));
            }
        }
        return dest;
    }

    public static BufferedImage flipVertical(BufferedImage source) {
        int w = source.getWidth();
        int h = source.getHeight();
        BufferedImage dest = new BufferedImage(w, h, source.getType());
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                dest.setRGB(j, h - 1 - i, source.getRGB(j, i));
            }
        }
        return dest;
    }

    public static BufferedImage flipHorizontal(BufferedImage source) {
        int w = source.getWidth();
        int h = source.getHeight();
        BufferedImage dest = new BufferedImage(w, h, source.getType());
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                dest.setRGB(w - 1 - j, i, source.getRGB(j, i));
            }
        }
        return dest;
    }

    public static BufferedImage negative(BufferedImage source) {
        int w = source.getWidth();
        int h = source.getHeight();
        BufferedImage dest = new BufferedImage(w, h, source.getType());
        for (int i = 0; i < w; i++) {
            for (int j = 0; j < h; j++) {
                Color color = new Color(source.getRGB(i, j));
                Color negetiveColor = new Color(255 - color.getRed(), 255 - color.getGreen(), 255 - color.getBlue());
                dest.setRGB(i, j, negetiveColor.getRGB());
            }
        }
        return dest;
    }

    public static BufferedImage normalize(BufferedImage source) {
        int w = source.getWidth();
        int h = source.getHeight();
        BufferedImage dest = new BufferedImage(w, h, source.getType());
        int[][] minMax = getMinMax(source);

        for (int i = 0; i < w; i++) {
            for (int j = 0; j < h; j++) {
                Color color = getNormalize(new Color(source.getRGB(i, j)), minMax);
                dest.setRGB(i, j, color.getRGB());
            }
        }
        return dest;
    }

    // calculate overall minimum and maximum of the image in each chanel
    private static int[][] getMinMax(BufferedImage source) {
        // [R,G,B][min,max]
        int[][] minMax = new int[3][2];

        for (int i = 0; i < 3; i++) {
            minMax[i][0] = 255;
            minMax[i][1] = 0;
        }
        for (int i = 0; i < source.getWidth(); i++) {
            for (int j = 0; j < source.getHeight(); j++) {
                Color color = new Color(source.getRGB(i, j));
                int r = color.getRed();
                int g = color.getGreen();
                int b = color.getBlue();

                // calculate minimum values
                if (r < minMax[0][0]) {
                    minMax[0][0] = r;
                }
                if (g < minMax[1][0]) {
                    minMax[1][0] = g;
                }
                if (b < minMax[2][0]) {
                    minMax[2][0] = b;
                }

                // calculate maximum values
                if (r > minMax[0][1]) {
                    minMax[0][1] = r;
                }
                if (g > minMax[1][1]) {
                    minMax[1][1] = g;
                }
                if (b > minMax[2][1]) {
                    minMax[2][1] = b;
                }
            }
        }
        return minMax;
    }

    private static Color getNormalize(Color color, int[][] minmax) {
        return new Color(
                (color.getRed() - minmax[0][0]) * 255 / (minmax[0][1] - minmax[0][0]),
                (color.getGreen() - minmax[1][0]) * 255 / (minmax[1][1] - minmax[1][0]),
                (color.getBlue() - minmax[2][0]) * 255 / (minmax[2][1] - minmax[2][0]),
                color.getAlpha());
    }

    // The average grayscale method
    public static BufferedImage averageGrayscale(BufferedImage source) {

        int a, r, g, b;
        int avgVal;
        int w = source.getWidth();
        int h = source.getHeight();

        BufferedImage dest = new BufferedImage(w, h, source.getType());
        // pre caluculation for faster averaging
        int[] avgLUT = new int[766];
        for (int i = 0; i < avgLUT.length; i++) {
            avgLUT[i] = (int) (i / 3);
        }

        for (int i = 0; i < w; i++) {
            for (int j = 0; j < h; j++) {

                // Get pixels by R, G, B
                Color color = new Color(source.getRGB(i, j));
                a = color.getAlpha();
                r = color.getRed();
                g = color.getGreen();
                b = color.getBlue();

                avgVal = avgLUT[r + g + b];
                Color c = new Color(avgVal, avgVal, avgVal, a);
                dest.setRGB(i, j, c.getRGB());

            }
        }
        return dest;
    }

    public static BufferedImage crop(BufferedImage source, int x, int y, int width, int height) {
        BufferedImage dest = new BufferedImage(width, height, source.getType());
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                dest.setRGB(i, j, source.getRGB(x + i, y + j));
            }
        }
        return dest;
    }
}
